package DistSys;

import java.util.*;
import java.awt.*;
import javax.swing.*;

import Element.*;
import Element.Geom.*;
import BackGround.*;

public class Sys {

    // background image list
    public static Vector imgList = new Vector(100,100);
    // background line list
    public static Vector bgElemList= new Vector(100,100);

    // foreground elem list & on screen elem list (also foreground)
    public static Vector elemList = new Vector(1000,1000);
    public static Vector screenElemList = new Vector(500,500);

    //selected elem list
    public static Vector selectedList = new Vector(100,100);

    public static Vector highLightedElemList = new Vector(10,10);
    public static Vector srcList = new Vector(10,10);
    public static AbsElem focusElem = null;

    private static boolean flag_VoltageMismatch=false;
    public static Vector voltageLevels = new Vector(10,10);

    public static double DOUBLE_COMPARE = 0.001; // 1 per mille

    public static Rectangle draggedRect = new Rectangle(); // for dragged Rect
    public static Point prevViewPosition= new Point(0,0);

    public static int SYS_MAX_X=1000, SYS_MAX_Y=1000;
    public static int SYS_MIN_X=0, SYS_MIN_Y=0;
    public static boolean isInsertNodeMode=false;
    public static boolean isTopoRun = false;
    public static boolean isRadial = false;

    public static String dvdFileName;  //for version 2 only

    public static ProjectInfo prjInfo = new ProjectInfo();
    public static PersonalInfo personalInfo = null;
    public static Vector costSumCases = new Vector();

    public static Frame mainFrame;


  public Sys() { }

  public static void setPrevViewPosition( int a, int b){
      prevViewPosition.x =a; prevViewPosition.y =b;
  }

  public static void reset(){
      imgList.removeAllElements();
      bgElemList.removeAllElements();
      elemList.removeAllElements();
      selectedList.removeAllElements(); //IndexOfCurrElem =-1;
      srcList.removeAllElements();
      screenElemList.removeAllElements();
      focusElem = null;
      Templates.reset();

      prjInfo.init();

      isInsertNodeMode = false;
      isTopoRun = false;

      prevViewPosition.x =0;  prevViewPosition.y =0;
      dvdFileName=null;
  }

  /**
   *If background layer is active, search background elements.
   *Otherwise,
   *Find the first element in selectedList which covers Point(x,y);
   *If N/A, find the first one in screenElemList which covers Point(x,y)
   *based on Screen Values. That is, an element already selected has higher
   *priority thatn other in-screen elements
   */
  public static AbsElem findElem(int x, int y){
    AbsElem elem;
    AbsElemGeom geom;
    Vector v;
    if     (Sys.prjInfo.iActiveLayer==ProjectInfo.BG_IMAGE_LAYER){ v=Sys.imgList; }
    else if(Sys.prjInfo.iActiveLayer==ProjectInfo.BG_LINE_LAYER) { v=Sys.bgElemList; }
    else {
        v = (Vector) Sys.selectedList.clone();
        v.addAll( Sys.screenElemList );
    }

    int iLayer;
    Layer lyr;
    for(int i=0; i<v.size();i++){
        elem=(AbsElem) v.elementAt(i);
        geom= elem.getElemGeomObj();
        iLayer = (int) Math.round( elem.getData(AbsElem.DATA_LAYER) ); //guaranteed precision
        iLayer += ProjectInfo.NUM_BACK_GND_LAYER; //offset by 2;
        lyr = (Layer) Sys.prjInfo.layerList.get( iLayer );
        if( lyr.getMode()==Layer.ON && geom.coverPoint(x,y,SysConst.SNAP_SIZE)){
            return elem;
        }
    }
    return null;
  }

  public static void findElemInRect(Rectangle r){
      AbsElem theElem;
      AbsElemGeom theGeom;
      Vector v;
      if     (Sys.prjInfo.iActiveLayer==ProjectInfo.BG_IMAGE_LAYER){ v=Sys.imgList; }
      else if(Sys.prjInfo.iActiveLayer==ProjectInfo.BG_LINE_LAYER) { v=Sys.bgElemList; }
      else {  v = Sys.elemList; }

      for (int i=0;i<v.size();i++){
          theElem=(AbsElem) v.elementAt(i);
          theGeom=(AbsElemGeom) theElem.getElemGeomObj();
          if( theGeom.isFullyInARect(r) == true){
              addElemToSelection( theElem); //add & set isSelected if not in selectedList
          }
      }
  }

  //make a copy of all selected elements;
  //set those newly-duplicated elements as the set of selection
  public static void copySelectedElem(){
      Vector tmpVector= (Vector) Sys.selectedList.clone();
      clearSelectionAndFocus();

      for(int i=0;i<tmpVector.size();i++){
           AbsElem e, e2;
           e2 = (AbsElem) tmpVector.elementAt(i);
           e = (AbsElem) e2.clone(); //polymorphism !!!
           if( Sys.prjInfo.iActiveLayer == ProjectInfo.BG_IMAGE_LAYER){
                imgList.add( e ); }
           else if( Sys.prjInfo.iActiveLayer == ProjectInfo.BG_LINE_LAYER){
                bgElemList.add( e ); }
           else { elemList.add(e); }
           addElemToSelection(e);
      }
      tmpVector.removeAllElements();
  }

  public static void toggleSelectedElem(){
      toggleElemList ( selectedList );
  }

  public static void toggleElemList(Vector v){
      AbsElem elem;
      AbsElemGeom geom;
      for(int i=0; i<v.size(); i++){
          elem = (AbsElem) v.get(i);
          geom = elem.getElemGeomObj();
          geom.toggle();
          if( elem instanceof Line){
              if( ((LineGeom)geom).getIsCable() ){
                  elem.setDftTemp(Line.objActiveTemp2);
                  Templates.copyAllDataFromTemp(elem, Line.objActiveTemp2);
              } else{
                  elem.setDftTemp(Line.objActiveTemp);
                  Templates.copyAllDataFromTemp(elem, Line.objActiveTemp);
              }
          }
      }
  }//end of toggleElemList(Vector v)


  public static AbsElem getElemFromIndex( int id ){
      if( Sys.prjInfo.iActiveLayer == ProjectInfo.BG_IMAGE_LAYER ) {
          return (AbsElem) imgList.get(id);
      } else if( Sys.prjInfo.iActiveLayer == ProjectInfo.BG_LINE_LAYER ){
          return (AbsElem) bgElemList.get(id);
      } else {
          return (AbsElem) elemList.get(id);
      }
  }

  public static void addElemInVector( Vector v, boolean bOnScreen, boolean bSelected ){
      AbsElem elem;
      for(int i=0; i< v.size(); i++){
          elem = (AbsElem) v.get(i);
          addElem( elem, bOnScreen, bSelected);
      }
  }

  public static void addElem( AbsElem elem, boolean bOnScreen, boolean bSelected ){
      //Before undo/redo zooming, this block has to be added.
      //The reason is that during moving zoomingFactor may be changed
      AbsElemGeom geom = elem.getElemGeomObj();
      geom.setNode1FromStored( geom.x1Stored, geom.y1Stored);
      geom.setNode2FromStored( geom.x2Stored, geom.y2Stored);
      if( geom instanceof ThreeNodeElemGeom ){
          ((ThreeNodeElemGeom)geom).findNode3BasedonScreen(); }

      clearConn(elem);
      if(Sys.prjInfo.iActiveLayer == ProjectInfo.BG_IMAGE_LAYER ){
          imgList.add( elem );
      } else if(Sys.prjInfo.iActiveLayer == ProjectInfo.BG_LINE_LAYER ){
          bgElemList.add( elem );
      } else{ //foreground layers
          elemList.add(elem);
          if( !(elem instanceof Group)) { updateConn(elem); }
          if( bOnScreen) { screenElemList.add( elem ); }
      }

      Sys.updateMaxAndMin( elem );
      if( bSelected) { Sys.addElemToSelection( elem ); }
      else { elem.getElemGeomObj().isSelected = false; } //ensure sync.
  }

  public static void addElemInAGroupIntoSelection(Group group){
      Vector vTmp = group.getContainedElemList();
      AbsElem elem;
      for(int i=0;i<vTmp.size();i++){
          elem = (AbsElem)vTmp.elementAt(i);
          if(Sys.selectedList.contains(elem)==false){
              Sys.addElemToSelection(elem);
          };
      }
  }

  //reset the selected list; also reset the Selection flag in each elem.geo
  // no "real" element is deleted from elemList here.
  public static void clearSelection(){
      for( int i =0; i <selectedList.size(); i++){
          AbsElem theElem = (AbsElem) selectedList.elementAt(i);
          AbsElemGeom theGeom=(AbsElemGeom) theElem.getElemGeomObj();
          theGeom.setSelected(false);
      }
      selectedList.removeAllElements();  // reset the selectedList
  }

  public static void clearSelectionAndFocus(){
      clearSelection();
      focusElem = null;
  }

  public static void removeElemFromSelection(AbsElem elem){
      if( selectedList.contains(elem)==true){
          AbsElemGeom geom=(AbsElemGeom) elem.getElemGeomObj();
          geom.setSelected(false);
          selectedList.remove(elem);
      }
  }

  //add theElem to selectedList AND set its "isSelected" to "true"
  //no duplication
  public static void addElemToSelection( AbsElem elem ){
      if( elem == null ) { return; }

      if( selectedList.contains(elem)==false){
          AbsElemGeom geom=(AbsElemGeom) elem.getElemGeomObj();
          geom.setSelected(true);
          selectedList.addElement(elem);
      }
  }

//  /**
//     *  Used by undo/redo. Since a copy of moved(rotated) elements must be
//     *  retained in the EditActionQueue, an exact copy is required.
//     *  AbsElem.clone() may shift the element.
//     *  See AbsElemGeom.clone()
//     */
//  public static Vector cloneElemWithUidForSelectedList(){
//      return cloneElemWithUidForVector( Sys.selectedList );
//  }//end of cloneElemWithUidForSelectedList();
//
//  public static Vector cloneElemWithUidForVector(Vector vSrc){
//      AbsElem elem;
//      Vector vRtn =  new Vector( vSrc.size() );
//      for(int i=0; i< vSrc.size(); i++){
//          elem = (AbsElem) vSrc.get(i);
//          vRtn.add( new ElemForUndoRedo(elem) );
//      }//end of for loop
//      return vRtn;
//  }//end of cloneElemWithUidForVector();

//  public static Object cloneElemWithUid(AbsElem elem ){
//      Vector vTmp = getActiveElemList();
//      ElemForUndoRedo elem2 = new ElemForUndoRedo(elem);
//      return elem2;
//  }//end of cloneElemWithUid();

  public static Vector getActiveElemList(){
      if( prjInfo.iActiveLayer == ProjectInfo.BG_IMAGE_LAYER ){
          return imgList;
      } else if ( prjInfo.iActiveLayer == ProjectInfo.BG_LINE_LAYER ) {
          return bgElemList;
      } else {
          return elemList;
      }
  }

  public static void displaceSelectedElem(int dx, int dy){
      displaceElemInVector( selectedList, dx, dy );
  }

  public static void displaceElemInVector(Vector v, int dx, int dy){
      AbsElem elem;
      for(int i=0; i<v.size(); i++){
          elem = (AbsElem) v.get(i);
          elem.getElemGeomObj().displace(dx, dy);
      }
  }


  //find the connectivity info for each elem
  //this is used for a legacy system without conn1,conn2 in the DB
  public static void findSysConn(){
      AbsElem e,e2;
      AbsElemGeom g;

      SysConst.progress = 0; // min
      for(int i=0; i<elemList.size();i++){
          e=(AbsElem) elemList.elementAt(i);
          g=e.getElemGeomObj();

          g.conn1.removeAllElements();
          g.conn2.removeAllElements();
          if( g instanceof ThreeNodeElemGeom){ ((ThreeNodeElemGeom)g).conn3.removeAllElements();}

          //clearConn(e); //same as above 3 line; but above approach is faster;
      }
      SysConst.progress = 1; // min

    //the NxN steps to build connectivity
      int sz = elemList.size();
      for(int i=0;i<sz;i++){
          SysConst.progress = (int) Math.round( 1.0 + 19.0*i/sz);
          e=(AbsElem) elemList.elementAt(i);
          for(int j=i+1;j<sz;j++){
              e2=(AbsElem)elemList.elementAt(j);
              findConnForTwoElem( e, e2);
          }
      }
      SysConst.progress = 20; // max
      isTopoRun = true;

/*
      // Nxlog(N) step to build topo
      int key;
      Integer KEY;
//      StringTokenizer st;
//      String str;
      Vector tmpV;
      Vector candidate = new Vector();
      //System.out.println("here");
      for(int i=0;i<elemList.size();i++){
          e=(AbsElem) elemList.elementAt(i);
          if( i%100 == 0 ) System.out.println("elem="+i);
          if( e instanceof Bus) {
              for(int j=0;j<elemList.size();j++){
                  e2=(AbsElem)elemList.elementAt(j);
                  if( e2 instanceof Bus && elemList.indexOf(e2)< elemList.indexOf(e) ){
                    //do nothing; preventing duplication when two bus are connected.
                    ;
                  }
                  else findConnForTwoElem( e, e2);
              }
              continue;
          }

          //for non-Bus elem
          for(int j=0;j<2;j++){
              if(j==0) key =  e.getElemGeomObj().x1Stored;
              else{
              //if( e.getElemGeomObj().x2Stored != e.getElemGeomObj().x1Stored){
                  key =  e.getElemGeomObj().x2Stored;
              //}
              //else continue;
              }

              Object o = Sys.treeMap.get( new Integer (key));
              if( o != null ){
                  //str = (String) o;
                  //st = new StringTokenizer(str,";");
                  //while (st.hasMoreTokens()) {
                  //    int elemID = Integer.parseInt(st.nextToken());
                  //    e2 = (AbsElem ) Sys.elemList.elementAt( elemID );
                  //    if( candidate.contains(e2)!=true ) { //guarantee no duplication
                  //        candidate.add(e2);
                  //    }
                  //}
                  tmpV = (Vector) o;
                  for(int k=0;k< tmpV.size();k++){
                      e2 = (AbsElem) tmpV.elementAt(k);
                      if( candidate.contains(e2)==false) candidate.add(e2); //no duplication
                  }
              }//end of if
          }//end of inner for

          candidate.remove( e );//remove myself
          for(int j=0;j<candidate.size();j++){
              e2 = (AbsElem) candidate.elementAt(j);
              if( elemList.indexOf(e2) > elemList.indexOf(e) ){
              //With this if, findConnForTwoElem( e2, e) won't be called again.
              //!!! otherwise duplication in conn1 and conn2
                  if( !(e2 instanceof Bus) ) findConnForTwoElem(e, e2);
              }
          }//2nd inner for loop
      }//outter for loop

      System.out.println("All Done:" + Sys.elemList.size());
*/

  }

    public static void assignVoltageLevelToAllElements(boolean bBlockMsg){
        Vector mySrcList = new Vector();
        // initializing all elements (important for disconnected elements)
        for(int i=0 ; i<Sys.elemList.size() ; i++) {
            AbsElem elem = (AbsElem) Sys.elemList.elementAt(i);
            //CktLaw ckt = elem.getCktLawObj();
            if (elem instanceof Source) {
                // sources are the only elements with voltages assigned
                elem.setResult( AbsElem.RES_NOMINAL_VOLT,
                    elem.getData(AbsElem.DATA_SRC_VOLT) );
                mySrcList.add( elem );
            } else
                elem.setResult( AbsElem.RES_NOMINAL_VOLT, 0.0);
        }//end of for
        // reset the list of voltage levels
        Sys.voltageLevels.clear();
        // reset of voltage level mismatch flag
        Sys.flag_VoltageMismatch=false;

        if(mySrcList.isEmpty()) {
            if(!bBlockMsg) {
                JOptionPane.showMessageDialog(mainFrame,
                    "No Sources to calculate Voltage Levels!");
            }
            return ;
        }
        for(int i=0;i<mySrcList.size();i++){
            AbsElem theSrc = (AbsElem) mySrcList.elementAt(i);
            TraceSource.calculateVoltageLevelOfElements(theSrc);
        }// end of for
        // the vector with voltage levels in Sys.java must be sorted
        // this ensures that the output of shadeByNomVoltage always has the same structure
        Collections.sort(Sys.voltageLevels);
        if(Sys.flag_VoltageMismatch && !bBlockMsg){
            JOptionPane.showMessageDialog(mainFrame,
                "There are voltage mismatches in the system!\n" +
                "The displayed results may be wrong!",
                "Warning Message", JOptionPane.WARNING_MESSAGE);
        }
   }//end of assignVoltageLevelToAllElement


  public static int buildSystemTopo (){
      AbsElem elem;
      AbsElemGeom geom;


      Sys.findAllSource();

      if(Sys.srcList.size()==0) { return 1;}

      //clean up the topology
      for(int i=0;i<Sys.elemList.size();i++){
          elem = (AbsElem) Sys.elemList.elementAt(i);
          geom = elem.getElemGeomObj();

          //commented becoz conn already built after reading DB
          //geom.conn1.removeAllElements();
          //geom.conn2.removeAllElements();

          geom.parent = null;
          geom.childList.removeAllElements();
          geom.sourceNode =0;

          if( geom instanceof SwtGeom ) { ((SwtGeom)geom).parent2=null; }
          if( elem instanceof Switch3 ) { ((ThreeNodeElemGeom)geom).parent2 =null;}
          if( geom instanceof BusGeom ) { ((BusGeom)geom).setIsVisited(false);}
      }//end of for

      //Sys.findSysConn();  //commented becoz conn already built after reading DB

      Sys.isRadial=true;
      for(int i=0;i<Sys.srcList.size();i++){
            AbsElem theSrc = (AbsElem) Sys.srcList.elementAt(i);
            theSrc.getElemGeomObj().sourceNode=2;
            int res = Sys.buildSubTree(theSrc);
            if( res > 1){
                Sys.isRadial=false;
                return res;
            }
      }// end of for

      return 0;  // radial tree build;
  }//end of buildSystemTopo()

  /*public static int buildTree2(Bus e){
        AbsElemGeom g = e.getElemGeomObj();
        Vector v;
        AbsElem e2;
        AbsElemGeom g2;
        int res;

        ((BusGeom)g).setIsVisited(true);

        //Bus.geom.conn1 contains all connected non-Bus elements
        for( int j=0;j<g.conn1.size();j++){
            e2 = (AbsElem) g.conn1.elementAt(j);
            g2= e2.getElemGeomObj();
            if( g2.childList.contains(e) ) continue; // in g.conn1, one elem is g's parent.

            if( e2 instanceof Source ) return 3; //loop found

            //for open switch: no recursion for an open swt
            if( (g2 instanceof SwtGeom) && ((SwtGeom)g2).getIsOpen()==true ){
                SwtGeom gS = (SwtGeom)g2;
                if( gS.parent ==null) { gS.parent = e; g.childList.add(e2); continue;}
                else if( gS.parent2 == null ) { gS.parent2 =e; g.childList.add(e2); continue; }
                else return 4;  //loop founded
            }

            //for close switch or any other elements (Closed switches perfom the
            //same as other elements in sense of building a tree
            if( g2.parent != null) {  return 3; } // loop founded
            else { g2.parent = e; g.childList.add(e2); }

            if( g2.conn1.contains( e ) ){ g2.sourceNode = 1;}
            else if( g2.conn2.contains(e) ){ g2.sourceNode =2;}
            else { return 2;} //error occured

            res = buildTree( e2);  //do recursion for closed swt & other elems
            if( res > 1){ return res;}

        }// for g.conn1

        //Bus.geom.conn2 contains all connected Buses
        for(int j=0;j<g.conn2.size();j++){
            Bus b3=(Bus) g.conn2.elementAt(j);
            BusGeom g3=(BusGeom) b3.getElemGeomObj();
            if( g3.getIsVisited()==true && g3.conn2.contains(e) ){
                continue; //for the case if two buses are connected
            }
            else {
                g3.parent = e;
                g.childList.add(b3);
                res = buildTree( (Bus) b3 );   //recurse if this Bus is not a parent Bus
                if( res >1 ) return res; //error occured
            }
        }//for g.conn2

        return 1;  // end of the execution if e2 is a Bus
  }//end of buildTree2(Bus e)
*/


  public static void findAllSource(){
      Sys.srcList.removeAllElements();
      for(int i=0;i<elemList.size();i++){
          AbsElem elem = (AbsElem)elemList.elementAt(i);
          if( elem instanceof Source ){
              srcList.add( elem );
          }
      }
  }

  public static void findIsolated(){
      AbsElem elem;
      AbsElemGeom geom;
      for (int i=0;i<Sys.elemList.size();i++){
          elem = (AbsElem) Sys.elemList.elementAt(i);
          geom = (AbsElemGeom) elem.getElemGeomObj();
          if( geom.parent == null && !(elem instanceof Source) ){
              Sys.addElemToSelection( elem );
          }
      }
  }


    /**
     * The class <code>TraceSource</code> contains the methods
     * <code>selectTraceToSource</code> and
     * <code>selectTraceAwayFromSource</code>.
     */
    public static class TraceSource {
        /**
         * Selects all possible traces to all sources based on the connectivity.
         *
         * @param   root   the root element which is starting point
         */
        public static void selectTraceToSource(AbsElem root) {
            vTSElems = new Vector(200);
            AbsElemGeom geom = root.getElemGeomObj();
            // trace all connected elements
            Vector vConn = (Vector)geom.conn1.clone();
            vConn.addAll(geom.conn2);
            if (geom instanceof ThreeNodeElemGeom)
                vConn.addAll(((ThreeNodeElemGeom)geom).conn3);
            // build trace "tree"
            trace(root, vConn, TRACE_NONE);
            // select "tree"
            select();
            // let the garbage collection do its work
            vTSElems = null;
        }

        /**
         * Selects all reachable elements if the given element is a source, or
         * all traces that do not lead to a source otherwise.
         *
         * @param   root   the root element which is starting point
         */
        public static void selectTraceAwayFromSource(AbsElem root) {
            vTSElems = new Vector(200);
            AbsElemGeom geom = root.getElemGeomObj();
            if (root instanceof Source) {
                // trace all connected elements
                Vector vConn = (Vector)geom.conn1.clone();
                vConn.addAll(geom.conn2);
                if (geom instanceof ThreeNodeElemGeom)
                    vConn.addAll(((ThreeNodeElemGeom)geom).conn3);
                // select trace "tree" while building it
                trace(root, vConn, TRACE_SELECT);
            } else if (root instanceof Bus) {
                // check every connected element separately
                Vector vConn = (Vector)geom.conn1.clone();
                vConn.addAll(geom.conn2);
                if (geom instanceof ThreeNodeElemGeom)
                    vConn.addAll(((ThreeNodeElemGeom)geom).conn3);
                Vector vTemp = new Vector(1);
                vTemp.setSize(1);
                for (int i = 0; i < vConn.size(); i++) {
                    vTemp.setElementAt(vConn.elementAt(i), 0);
                    if (!trace(root, vTemp, TRACE_NONE))
                        // if no source is found
                        // select the trace "tree" while rebuilding it
                        trace(root, vTemp, TRACE_SELECT);
                }
            } else {
                // for all conn sides check whether a source is found
                if (!trace(root, geom.conn1, TRACE_NONE))
                    // if no source is found
                    // select the trace "tree" while rebuilding it
                    trace(root, geom.conn1, TRACE_SELECT);
                if (!trace(root, geom.conn2, TRACE_NONE))
                    trace(root, geom.conn2, TRACE_SELECT);
                if (geom instanceof ThreeNodeElemGeom) {
                    ThreeNodeElemGeom geom3 = (ThreeNodeElemGeom)geom;
                    if (!trace(root, geom3.conn3, TRACE_NONE))
                        trace(root, geom3.conn3, TRACE_SELECT);
                }
            }
            // let the garbage collection do its work
            vTSElems = null;
        }

        public static void calculateVoltageLevelOfElements(AbsElem root) {
            if (root instanceof Source) {
                vTSElems = new Vector(200);
                AbsElemGeom geom = root.getElemGeomObj();
                Vector vConn = (Vector)geom.conn1.clone();
                vConn.addAll(geom.conn2);
                trace(root, vConn, TRACE_VOLTAGE_LEVEL);
                // let the garbage collection do its work
                vTSElems = null;
            }
            else {//this should never happen
                JOptionPane.showMessageDialog(mainFrame,
                    "Error: Element is not a Source!");
            }
        }//end of calculateVoltageLevelOfElements

        public interface CoordinationList {
            /**
             * Called during <code>getCoordinationOfSource/code> whenever the
             * last child of a protection device is traced.
             *
             * @param  vElems  the vector containing all elements on the path
             *                 from the source to this protection device
             *                 NOTE: You must not change this vector
             * @param  vProtectionElems  the vector containing all protection
             *                           devices on the path from the source
             *                           NOTE: You must not change this vector
             */
            public void add(Vector vElems, Vector vProtectionElems);
        }

        /**
         * Searches for coordination problems which are too many protection
         * devices in a possible current path.
         *
         * @param   root       the root element which is starting point
         * @param   coordList  the interface called when the last child of a
         *                     protection device is traced
         * @see CoordinationList
         */
        public static void getCoordinationOfSource(AbsElem root,
            CoordinationList coordList) {
            if (!(root instanceof Source))
                return;
            coordinationList = coordList;
            vProtectionElems = new Vector(10, 2);

            vTSElems = new Vector(200);
            AbsElemGeom geom = root.getElemGeomObj();
            // trace all connected elements
            Vector vConn = (Vector)geom.conn1.clone();
            vConn.addAll(geom.conn2);
            // select trace "tree" while building it
            trace(root, vConn, TRACE_COORDINATION);
            // let the garbage collection do its work
            vTSElems = null;
            vProtectionElems = null;
            coordinationList = null;
        }

        /**
         * Action: no action
         */
        private static final int TRACE_NONE          = 0;
        /**
         * Action: select all handled elements
         */
        private static final int TRACE_SELECT        = 1;
        /**
         * Action: determine voltage level
         */
        private static final int TRACE_VOLTAGE_LEVEL = 2;
        /**
         * Action: check for too many protection devices
         */
        private static final int TRACE_COORDINATION  = 3;
        /**
         * Builds the trace "tree" and selects it if specified. Actually the tree
         * is no real tree since it could contain loop and multiple roots.
         *
         * @param   root     the root element which is starting point
         * @param   vConn    a vector of connected elements to trace
         * @param   iAction  specifies which action should be performed
         */
        private static boolean trace(AbsElem root, Vector vConn, int iAction) {
            // clear child list and parent for all elements so that we can trace
            // without considering old results
            for (int i = 0; i < Sys.elemList.size(); i++) {
                AbsElem elem = (AbsElem)Sys.elemList.elementAt(i);
                AbsElemGeom geom = elem.getElemGeomObj();
                geom.parent = null;
                geom.childList.clear();
            }
            // remove all sources from list now so that the list afterwards
            // contains only the sources connected to this element
            srcList.removeAllElements();

            if (vConn == null || vConn.isEmpty()) {
                // if there are no connected elements we can stop tracing now
                // and we have not found a source
                return false;
            }

            TraceSourceElem tsElem = new TraceSourceElem();
            tsElem.elem = root;
            tsElem.geom = root.getElemGeomObj();
            tsElem.geom.childList.add(root);
            tsElem.conn = vConn;
            vTSElems.addElement(tsElem);

            // while there are still untraced elements ...
            while (!vTSElems.isEmpty()) {
                tsElem = (TraceSourceElem)vTSElems.lastElement();

                // check whether there are still untraced connected elements
                if (tsElem.iPos >= tsElem.conn.size()) {
                    // no more untraced connected elements so this element
                    // is traced and can be removed from the list
                    if (iAction == TRACE_COORDINATION &&
                        vProtectionElems.contains(tsElem.elem)) {
                        // but first we need to check whether it is a
                        // protection device and report it
                        coordinationList.add(vTSElems, vProtectionElems);
                        vProtectionElems.removeElementAt(
                            vProtectionElems.size()-1);
                    }
                    vTSElems.removeElementAt(vTSElems.size()-1);
                    continue;
                }

                // prepare the next connected element
                TraceSourceElem tsConn = new TraceSourceElem();
                tsConn.elem = (AbsElem)tsElem.conn.elementAt(tsElem.iPos);
                tsConn.geom = tsConn.elem.getElemGeomObj();
                tsElem.iPos++;

                // select the element (if specified)
                if (iAction == TRACE_SELECT)
                    addElemToSelection(tsConn.elem);

                // get the connected elements and the open/close status
                if (tsConn.geom instanceof TwoNodeElemGeom) {
                    if (tsConn.geom instanceof BusGeom) {
                        // all connections of a bus are kept in conn1
                        tsConn.conn = (Vector)tsConn.geom.conn1.clone();
                        // so we take conn1 and remove our "predecessor"
                        tsConn.conn.remove(tsElem.elem);
                    } else if (tsConn.geom instanceof SwtGeom &&
                               ((SwtGeom)tsConn.geom).getIsOpen() &&
                               tsConn.elem != root) {
                        // this switchable element is open so tracing should
                        // stop here the root element is treated as a closed
                        // element
                        continue;
                    } else {
                        // for all other two node elements we take the conn side
                        // that _not_ contains our "predecessor" (since all
                        // elements on that side are handled by our
                        // "predecessor")
                        if (tsConn.geom.conn1.contains(tsElem.elem))
                            tsConn.conn = tsConn.geom.conn2;
                        else
                            tsConn.conn = tsConn.geom.conn1;
                    }
                } else if (tsConn.geom instanceof ThreeNodeElemGeom) {
                    ThreeNodeElemGeom geom3 = (ThreeNodeElemGeom)tsConn.geom;
                    if (tsConn.elem instanceof Switch3 &&
                        tsConn.elem != root) {
                        // for transfer switches we have to look whether it is
                        // switched the root element is treated as a closed
                        // element
                        if (geom3.getIsSwitched()) {
                            // a switched transfer switch connected
                            // conn side 2 and 3 and keeps conn side 1 open
                            if (geom3.conn2.contains(tsElem.elem))
                                tsConn.conn = geom3.conn3;
                            else if (geom3.conn3.contains(tsElem.elem))
                                tsConn.conn = geom3.conn2;
                            else
                                // this side is open so tracing should stop here
                                continue;
                        } else {
                            // a unswitched transfer switch connected
                            // conn side 1 and 3 and keeps conn side 2 open
                            if (geom3.conn1.contains(tsElem.elem))
                                tsConn.conn = geom3.conn3;
                            else if (geom3.conn3.contains(tsElem.elem))
                                tsConn.conn = geom3.conn1;
                            else
                                // this side is open so tracing should stop here
                                continue;
                        }
                    } else {
                        // for all other three node elements we take the two conn sides that
                        // _not_ include our "predecessor"
                        if (geom3.conn1.contains(tsElem.elem)) {
                            tsConn.conn = (Vector)geom3.conn2.clone();
                            tsConn.conn.addAll(geom3.conn3);
                        } else {
                            tsConn.conn = (Vector)geom3.conn1.clone();
                            if (geom3.conn2.contains(tsElem.elem))
                                tsConn.conn.addAll(geom3.conn3);
                            else
                                tsConn.conn.addAll(geom3.conn2);
                        }
                    }
                } else if (tsConn.geom instanceof OneNodeElemGeom) {
                    if (tsConn.elem instanceof Source)
                        // sourced have to be added to the source list
                        srcList.add(tsConn.elem);
                    // else
                    //  all other one node element need no special handling
                }

                if (tsConn.geom.childList.isEmpty()) {
                    // no loop is detected
                    if (iAction == TRACE_COORDINATION) {
                        int iType = tsConn.elem.getType();
                        if (iType == SysConst.TYPE_BREAKER ||
                            iType == SysConst.TYPE_DRT_BREAKER ||
                            iType == SysConst.TYPE_FUSE ||
                            iType == SysConst.TYPE_SECTIONIZER ||
                            iType == SysConst.TYPE_ARRESTER ||
                            iType == SysConst.TYPE_RECLOSER) {
                            // this is a protection device
                            vProtectionElems.addElement(tsConn.elem);
                        }
                    }
                    if (tsConn.conn != null && !tsConn.conn.isEmpty()) {
                        // if there are connected elements and no loop is detected
                        // add this element to the list of untraced elements
                        vTSElems.addElement(tsConn);
                    }
                }

                if (iAction == TRACE_VOLTAGE_LEVEL){
                    //CktLaw cktChild=((AbsElem)tsConn.elem).getCktLawObj();
                    //CktLaw cktParent=((AbsElem)tsElem.elem).getCktLawObj();
                    double dChildVoltageLevel = tsElem.elem.getResult(AbsElem.RES_NOMINAL_VOLT);//cktParent.nominalVolt;
                    // trace approches a transformer from the secondary side (ratio is inverse)
                    // the child, which is a transformer, is processed
                    if (tsConn.elem instanceof Txfm &&
                        tsConn.geom.conn2.contains(tsElem.elem)){
                        dChildVoltageLevel /= tsConn.elem.getData(AbsElem.DATA_TXFM_RATIO); //cktChild.getData(CktLaw.TXFM_RATIO);
                    }
                    // trace approches a transformer from the primary side (normal ratio)
                    // transformer has the primary voltage, but children get new voltage
                    // the child, which is the first element behind the transformer, is processed
                    if (tsElem.elem instanceof Txfm &&
                        tsElem.geom.conn2.contains(tsConn.elem)){
                        dChildVoltageLevel *= tsElem.elem.getData(AbsElem.DATA_TXFM_RATIO);//cktParent.getData(CktLaw.TXFM_RATIO);
                    }
                    //assign the voltage level to the child
                    if (tsConn.elem.getResult( AbsElem.RES_NOMINAL_VOLT)==0.0)  //cktChild.nominalVolt == 0.0)// is zero?
                        tsConn.elem.setResult(AbsElem.RES_NOMINAL_VOLT, dChildVoltageLevel );//cktChild.nominalVolt = dChildVoltageLevel;// new value assigned
                    else if(Math.abs( tsConn.elem.getResult( AbsElem.RES_NOMINAL_VOLT)-dChildVoltageLevel)
                        > Math.abs(Sys.DOUBLE_COMPARE*dChildVoltageLevel)) {//the voltage level is not correct
                        Sys.flag_VoltageMismatch=true;
                    }
                    //add voltage level to the voltage level list
                    int i;
                    for(i=0 ; i<Sys.voltageLevels.size() ; i++) {
                        double voltageLev=((Double)Sys.voltageLevels.get(i)).doubleValue();
                        if(Math.abs(dChildVoltageLevel-voltageLev)//if list contains voltage level
                            < Math.abs(Sys.DOUBLE_COMPARE*dChildVoltageLevel))
                            break;
                    }//end of for-loop
                    if(i==Sys.voltageLevels.size()){
                        //add element only, if previous loop did not break
                        Sys.voltageLevels.add(new Double (dChildVoltageLevel));
                    }
                }

                // since we now know that the element/conn side is closed
                // we add our "predecessor" to our "child list"
                tsConn.geom.childList.add(tsElem.elem);
            }//end of while-loop

            // return whether we found sources or not
            return !srcList.isEmpty();
        }

        /**
         * Selects all elements of the trace "tree" that are reached (within the
         * tree) from a source.
         *
        * @see     DistSys.Sys.TraceSource.trace
         */
        private static void select() {
            TraceSourceElem tsElem = null;
            // start selecting at all found sources
            for (int i = 0; i < srcList.size(); i++) {
                // prepare the source
                tsElem = new TraceSourceElem();
                tsElem.elem = (AbsElem)srcList.elementAt(i);
                tsElem.geom = tsElem.elem.getElemGeomObj();
                // indicate that we handled this element
                tsElem.geom.parent = tsElem.elem;
                // select the element
                addElemToSelection(tsElem.elem);
                // add this element to the list of unselected elements
                vTSElems.addElement(tsElem);
            }

            // while there are still unselected elements ...
            while (!vTSElems.isEmpty()) {
                tsElem = (TraceSourceElem)vTSElems.lastElement();

                // prepare the next connected element
                TraceSourceElem tsChild = new TraceSourceElem();
                tsChild.elem = (AbsElem)tsElem.geom.childList.
                    elementAt(tsElem.iPos);
                tsChild.geom = tsChild.elem.getElemGeomObj();

                // check whether there are still unselected connected elements
                tsElem.iPos++;
                if (tsElem.iPos >= tsElem.geom.childList.size()) {
                    // no more unselected connected elements,
                    // therefore this element is considered selected and
                    // can be removed from the list
                    vTSElems.removeElementAt(vTSElems.size()-1);
                }

                // check whether we handled this element yet
                // (geom.parent is used as an indicator for that)
                if (tsChild.geom.parent == null) {
                    // indicate that we handled this element
                    tsChild.geom.parent = tsChild.elem;
                    // select the element
                    addElemToSelection(tsChild.elem);
                    // add this element to the list of unselected elements
                    vTSElems.addElement(tsChild);
                }
            }
        }
        /*
         * Internally used data structure
         */
        private static class TraceSourceElem {
            AbsElem elem = null;
            AbsElemGeom geom = null;
            Vector conn = null;
            int iPos = 0;
        }
        private static Vector vTSElems = null;
        private static Vector vProtectionElems = null;
        private static CoordinationList coordinationList = null;
    }

//  public static void findUpstream( AbsElem elem ){
//      int index;
//      AbsElemGeom geom;
//
//      //elem.parent could be NULL for an isolated elem
//      while( elem !=null && !(elem instanceof Source) ){
//          Sys.addElemToSelection(elem);
//          geom = elem.getElemGeomObj();
//
//          if( elem instanceof Switch3 ){
//              if( ((ThreeNodeElemGeom)geom).getIsSwitched()==true){//switched
//                  elem = ((ThreeNodeElemGeom)geom).parent2;
//              }
//              else { elem = geom.parent; }
//          }
//          else { elem = geom.parent; }
//      };
//      if( elem!=null) Sys.addElemToSelection( elem );
//  }
//
//  public static void findDownstream(AbsElem elem){
//      AbsElemGeom geom = elem.getElemGeomObj();
//      Sys.addElemToSelection(elem);
//      int j= elem.getElemGeomObj().childList.size();
//      for(int i=0; i<j; i++){
//          AbsElem theChild = (AbsElem) geom.childList.elementAt(i);
//          if( theChild instanceof Switch3){
//              ThreeNodeElemGeom g3 = (ThreeNodeElemGeom) theChild.getElemGeomObj();
//              if( g3.conn1.contains( elem ) && g3.getIsSwitched()==true ){
//                  continue;  //open point; no furthur recursion
//              }
//              else if( g3.conn2.contains( elem ) && g3.getIsSwitched()==false ){
//                  continue;  //open point; no furthur recursion
//              }
//          }
//
//          findDownstream(theChild);
//      }
//  }


  //delete one elem; put the last elem into the slot of this deleted elem
  //Then, the index of other elem will not be changed
  //This is useful in displaying elem info, particularly keeping consistency
  //after users delete some elem
  public static void deleteElem(AbsElem elem){
      int index = elemList.indexOf( elem );
      int sz = elemList.size();
      clearConn(elem);

      if(Sys.prjInfo.iActiveLayer == ProjectInfo.BG_IMAGE_LAYER ){
          if( Sys.imgList.contains( elem ) ) {
              Sys.imgList.remove( elem );
          }
      } else if(Sys.prjInfo.iActiveLayer == ProjectInfo.BG_LINE_LAYER ){
          if( Sys.bgElemList.contains( elem ) ) {
              Sys.bgElemList.remove( elem );
          }
      } else{
          elemList.remove(elem);
          if( index != (sz-1) ){  //elem is not the last elem; then swap this with last one
              AbsElem lastElem = (AbsElem) elemList.lastElement();
              elemList.remove( lastElem );
              elemList.add( index, lastElem);
          }
          //screen list is for foreground elem only , not for background elements
          if(Sys.screenElemList.contains(elem)){
              Sys.screenElemList.remove(elem);
          }
          if( Sys.highLightedElemList.contains( elem ) ){
              Sys.highLightedElemList.remove(elem);
          }
      }

      if(Sys.selectedList.contains(elem)){
          elem.getElemGeomObj().isSelected = false;
          Sys.selectedList.remove(elem);
      }

      if( focusElem == elem ) { focusElem = null; }
  }

  public static void deleteElemList( Vector v){
      for(int i=0; i< v.size(); i++){
          deleteElem ( (AbsElem) v.get(i) );
      }
  }

  public static void deleteSelectedElem(){
      //Must use vTmp to hold the selectedList
      //otherwise, deleteElem(elem) will remove one from selectedList.
      Vector vTmp = (Vector) selectedList.clone();
      AbsElem theElem;
      for( int i =0; i <vTmp.size(); i++){
          theElem = (AbsElem) vTmp.elementAt(i);
          deleteElem( theElem );
      }
      vTmp.removeAllElements();
  }

  // calculate the leftTop and bottomRight (x,y) for the whole system from elemList
  public static void calcMaxAndMin(){
      int minX, minY, maxX, maxY;
      Vector vAll = new Vector(imgList.size()+bgElemList.size()+elemList.size(),100);
      vAll.addAll( imgList);
      vAll.addAll( bgElemList);
      vAll.addAll( elemList );
      if(vAll.size() == 0 )  return;

      Rectangle rect = calcBoundRect( vAll);
      Sys.SYS_MIN_X = rect.x;
      Sys.SYS_MIN_Y = rect.y;
      Sys.SYS_MAX_X = rect.x + rect.width;
      Sys.SYS_MAX_Y = rect.y + rect.height;

      adjustNegativeXY( vAll );
      //adjustBigMinXY( vAll);
  }

  //adjust if there are negative numbers
  private static boolean adjustNegativeXY(Vector vAllList){
      int xOffset =0, yOffset=0;
      if( Sys.SYS_MIN_X>=0 && Sys.SYS_MIN_Y>=0) { return false;} //no adjustment

      if( Sys.SYS_MIN_X <0 ){ xOffset = (int) (-1.2 * Sys.SYS_MIN_X) ; }
      if( Sys.SYS_MIN_Y <0 ){ yOffset = (int) (-1.2 * Sys.SYS_MIN_Y) ; }
      adjustOffset( vAllList, xOffset, yOffset);
      return true;
  }

  //adjust if MinX and MinY too huge
  private static boolean adjustBigMinXY(Vector vAllList){
      int xOffset =0, yOffset=0;
      int THRESHOLD = 2000;

      if( Sys.SYS_MIN_X<=THRESHOLD && Sys.SYS_MIN_Y<=THRESHOLD) { return false;} //no adjustment

      if( Sys.SYS_MIN_X >THRESHOLD ){ xOffset = THRESHOLD- Sys.SYS_MIN_X; }
      if( Sys.SYS_MIN_Y >THRESHOLD ){ yOffset = THRESHOLD- Sys.SYS_MIN_Y; }
      adjustOffset( vAllList, xOffset, yOffset);
      return true;
  }

  //add xOffset to all x coordinates; add yOffset to all y coordinates
  private static void adjustOffset( Vector vAllList, int xOffset, int yOffset){
      AbsElemGeom geom;
      Sys.SYS_MIN_X += xOffset;
      Sys.SYS_MIN_Y += yOffset;
      Sys.SYS_MAX_X += xOffset;
      Sys.SYS_MAX_Y += yOffset;

      int sz = vAllList.size();
      for(int i=0; i<sz; i++){
          geom= ((AbsElem) vAllList.elementAt( i)).getElemGeomObj();
          geom.x1Stored += xOffset;
          geom.x2Stored += xOffset;
          geom.y1Stored += yOffset;
          geom.y2Stored += yOffset;
          if( geom instanceof ThreeNodeElemGeom){
              ThreeNodeElemGeom g3 = (ThreeNodeElemGeom) geom;
              g3.x3Stored += xOffset;
              g3.y3Stored += yOffset;
          }
      }
  }

  // update the leftTop and bottomRight (x,y) for the whole system as the elem
  // has been add to this sytem
  public static void updateMaxAndMin( AbsElem e){
      int tmp;
      int minX,minY,maxX,maxY;

      AbsElemGeom theGeom= e.getElemGeomObj();
      tmp  = Math.min( theGeom.getX1Stored(), theGeom.getX2Stored() );
      minX = Math.min( Sys.SYS_MIN_X, tmp);
      tmp  = Math.min( theGeom.getY1Stored(), theGeom.getY2Stored() );
      minY = Math.min(Sys.SYS_MIN_Y, tmp);

      tmp  = Math.max( theGeom.getX1Stored(), theGeom.getX2Stored() );
      maxX = Math.max(Sys.SYS_MAX_X, tmp);
      tmp  = Math.max( theGeom.getY1Stored(), theGeom.getY2Stored() );
      maxY = Math.max( Sys.SYS_MAX_Y, tmp);

      Sys.SYS_MIN_X = minX - (int)( (maxX-minX)*0.1);
      Sys.SYS_MIN_Y = minY - (int)( (maxY-minY)*0.1);
      Sys.SYS_MAX_X = maxX + (int)( (maxX-minX)*0.1);
      Sys.SYS_MAX_Y = maxY + (int)( (maxY-minY)*0.1);
  }

  //Calculate the boudning rectangle for all elem in the Vector v.
  //Stored values are used here.
  public static Rectangle calcBoundRect( Vector v){
      int minX, minY, maxX, maxY;
      if( v.size() == 0 ) { return  new Rectangle(0,0,0,0); }

      AbsElem elem = (AbsElem) v.elementAt( 0 );
      AbsElemGeom geom = elem.getElemGeomObj();
      minX= Math.min( geom.getX1Stored(), geom.getX2Stored() );
      minY= Math.min( geom.getY1Stored(), geom.getY2Stored() );
      maxX= Math.max( geom.getX1Stored(), geom.getX2Stored() );
      maxY= Math.max( geom.getY1Stored(), geom.getY2Stored() );

      int tmp;
      for( int i =1; i <v.size(); i++){
          geom= ((AbsElem) v.get( i)).getElemGeomObj();
          tmp  = Math.min( geom.getX1Stored(), geom.getX2Stored() );
          minX = Math.min( minX, tmp);
          tmp  = Math.min( geom.getY1Stored(), geom.getY2Stored() );
          minY = Math.min(minY, tmp);
          tmp  = Math.max( geom.getX1Stored(), geom.getX2Stored() );
          maxX = Math.max(maxX, tmp);
          tmp  = Math.max( geom.getY1Stored(), geom.getY2Stored() );
          maxY = Math.max( maxY, tmp);
      }

      //Add 10% buffer area at each side
      minX = minX - (int)( (maxX-minX)*0.1);
      minY = minY - (int)( (maxY-minY)*0.1);
      maxX = maxX + (int)( (maxX-minX)*0.1);
      maxY = maxY + (int)( (maxY-minY)*0.1);

      return new Rectangle(minX, minY, (maxX-minX), (maxY-minY) );
  }

  //maybe we need a method to update SYS_MIN_* & SYS_MAX_* when deleting a set of
  //elements, but this is not critical since a larger system boundary doesn't hurt
  // ( System boundary goes smaller after deleting elements ).
  // Users can press "Zoom All" to update boundary
  public static void updateMaxAndMinAfterDeleting(){}



  /**
   * Based on the new ZoomFactor :
   *  update the coordinates based on new scale for all elements
   */
  public static void updateAllCoord( Rectangle newRect ){
      AbsElem theElem;
      AbsElemGeom theGeom;

      int sz = elemList.size();
      for(int i=0; i<sz; i++){
          theElem=(AbsElem) elemList.elementAt(i);
          theGeom = theElem.getElemGeomObj();
          theGeom.scaleCoord(Sys.prjInfo.dZoomFactor); // find new (x,y)'s for elements
      }//end of for loop

      sz = Sys.bgElemList.size();
      for(int i=0; i<sz; i++){
          theElem=(AbsElem) bgElemList.get(i);
          theElem.getElemGeomObj().scaleCoord(Sys.prjInfo.dZoomFactor);
      }

      sz = Sys.imgList.size();
      for(int i=0; i<sz; i++){
          theElem=(AbsElem) imgList.get(i);
          theElem.getElemGeomObj().scaleCoord(Sys.prjInfo.dZoomFactor);
      }
  }//end of updateAllCoord()

  public static void updateScreenList( Rectangle rect){
      Sys.screenElemList.removeAllElements();
      AbsElem theElem;
      AbsElemGeom theGeom;
      for(int i=0;i<Sys.elemList.size();i++){
          theElem=(AbsElem) Sys.elemList.elementAt(i);
          theGeom=theElem.getElemGeomObj();
          if(theGeom.isPartlyInARect( rect )==true){
              Sys.screenElemList.add( theElem);
          }
      }
  }//end of updateScreenList


  //when updateing conn for a dragged element, call clearConn() before
  //call this method updateConn();
  //so far, returned boolean value isn't used in any case.
  public static boolean updateConn( AbsElem e){
      if( e instanceof BgLine || e instanceof BgImage) { return false; }
      AbsElem e2;
      boolean updated =false;
      //clearConn(e); //clear conn ; effective for "moving element" event only
      //will do starting node no matter "e" is one node element or not
      for(int i=0; i<elemList.size(); i++){
          e2=(AbsElem) elemList.elementAt(i);
          if( e2 == e ) continue; //same elem
          if( findConnForTwoElem( e, e2) ) updated = true;
      }//end of for loop
      return updated;
  }//end of updateConn();


  //clean connectivity info for one element only
  public static void clearConn(AbsElem e){
      if( e instanceof BgLine || e instanceof BgImage) { return; }
      Vector[] v = new Vector[3];
      AbsElem e2;
      AbsElemGeom g2;
      AbsElemGeom g=e.getElemGeomObj();

      int sz=2;
      v[0] = g.conn1;
      v[1] = g.conn2;
      if( g instanceof ThreeNodeElemGeom){
          v[2] = ((ThreeNodeElemGeom)g).conn3;
          sz=3;
      }

      for(int j=0; j< sz ;j++){
          for(int i=0;i<v[j].size();i++){
              e2 = (AbsElem) v[j].elementAt(i);
              g2 = e2.getElemGeomObj();
              if( g2 instanceof ThreeNodeElemGeom){
                  if( ((ThreeNodeElemGeom)g2).conn3.contains(e) ){
                      ((ThreeNodeElemGeom)g2).conn3.remove(e);
                      continue;
                  }
              }
              if( g2.conn1.contains( e ) )  g2.conn1.remove( e );
              else if( g2.conn2.contains( e ) )  g2.conn2.remove( e );
              else System.out.println("Error @ Sys.clearConn()-"+j+": "+g.getName()+",  "+g2.getName());  //not reachable
          }
          v[j].removeAllElements();
      }
  }//end of clearConn();


  /*======================================================
  findConnForTwoElem(AbsElem e1, AbsElem e2 )
  findConnForNodes(AbsElem e1, AbsElem e2, int i, int j)
  findConnForBusses( Bus b1, Bus b2)
  findConnForBusAndNode( Bus b, AbsElem e, int i)
  ======================================================*/
  private static boolean findConnForTwoElem(AbsElem e1, AbsElem e2 ){
      boolean snapped =false;
      AbsElemGeom g1 = e1.getElemGeomObj();
      AbsElemGeom g2 = e2.getElemGeomObj();

      if( e1 instanceof Group || e2 instanceof Group ) return false;

      if( g1 instanceof OneNodeElemGeom){
              if( g2 instanceof OneNodeElemGeom){
                  if( findConnForNodes( e1, e2, 1, 1) ) snapped = true;
              }
              else if( g2 instanceof BusGeom){
                  if( findConnForBusAndNode( (Bus) e2, e1, 1 ) ) snapped = true;
              }
              else if( g2 instanceof TwoNodeElemGeom ){
                  if( findConnForNodes( e1, e2, 1, 1) ) snapped = true;
                  if( findConnForNodes( e1, e2, 1, 2) ) snapped = true;
              }
              else if( g2 instanceof ThreeNodeElemGeom){
                  if( findConnForNodes( e1, e2, 1, 1) ) snapped = true;
                  if( findConnForNodes( e1, e2, 1, 2) ) snapped = true;
                  if( findConnForNodes( e1, e2, 1, 3) ) snapped = true;
              }
              else{
                  System.out.println("Error @ Sys.findConnForTwoElem() -1");
              }
          }
          else if ( g1 instanceof BusGeom){  // this must appear before next eles if
                                            // becoz BusGeom is a subclass of TwoNodeElemGeom
              if( g2 instanceof OneNodeElemGeom){
                  if( findConnForBusAndNode( (Bus) e1, e2, 1) ) snapped = true;
              }
              else if( g2 instanceof BusGeom){
                  if( findConnForBusses( (Bus)e1, (Bus) e2 ) ) snapped = true;
              }
              else if ( g2 instanceof TwoNodeElemGeom){
                  if( findConnForBusAndNode( (Bus) e1, e2, 1) ) snapped = true;
                  if( findConnForBusAndNode( (Bus) e1, e2, 2) ) snapped = true;
              }
              else if( g2 instanceof ThreeNodeElemGeom){
                  if( findConnForBusAndNode( (Bus) e1, e2, 1) ) snapped = true;
                  if( findConnForBusAndNode( (Bus) e1, e2, 2) ) snapped = true;
                  if( findConnForBusAndNode( (Bus) e1, e2, 3) ) snapped = true;
              }
              else{
                  System.out.println("Error @ Sys.findConnForTwoElem() -2");
              }
          }
          else if ( g1 instanceof TwoNodeElemGeom){
              if( g2 instanceof OneNodeElemGeom){
                  if( findConnForNodes( e1, e2, 1, 1) ) snapped = true;
                  if( findConnForNodes( e1, e2, 2, 1) ) snapped = true;
              }
              else if( g2 instanceof BusGeom){
                  if( findConnForBusAndNode( (Bus) e2, e1, 1) ) snapped = true;
                  if( findConnForBusAndNode( (Bus) e2, e1, 2 ) ) snapped = true;
              }
              else if( g2 instanceof TwoNodeElemGeom ){
                  if( findConnForNodes( e1, e2, 1, 1) ) snapped = true;
                  if( findConnForNodes( e1, e2, 1, 2) ) snapped = true;
                  if( findConnForNodes( e1, e2, 2, 1) ) snapped = true;
                  if( findConnForNodes( e1, e2, 2, 2) ) snapped = true;
              }
              else if( g2 instanceof ThreeNodeElemGeom){
                  if( findConnForNodes( e1, e2, 1, 1) ) snapped = true;
                  if( findConnForNodes( e1, e2, 1, 2) ) snapped = true;
                  if( findConnForNodes( e1, e2, 1, 3) ) snapped = true;
                  if( findConnForNodes( e1, e2, 2, 1) ) snapped = true;
                  if( findConnForNodes( e1, e2, 2, 2) ) snapped = true;
                  if( findConnForNodes( e1, e2, 2, 3) ) snapped = true;
              }
              else{
                  System.out.println("Error @ Sys.findConnForTwoElem() -3");
              }
          }
          else if ( g1 instanceof ThreeNodeElemGeom ){
              if( g2 instanceof OneNodeElemGeom){
                  if( findConnForNodes( e1, e2, 1, 1) ) snapped = true;
                  if( findConnForNodes( e1, e2, 2, 1) ) snapped = true;
                  if( findConnForNodes( e1, e2, 3, 1) ) snapped = true;
              }
              else if( g2 instanceof BusGeom){
                  if( findConnForBusAndNode( (Bus) e2, e1, 1) ) snapped = true;
                  if( findConnForBusAndNode( (Bus) e2, e1, 2 ) ) snapped = true;
                  if( findConnForBusAndNode( (Bus) e2, e1, 3 ) ) snapped = true;
              }
              else if( g2 instanceof TwoNodeElemGeom ){
                  if( findConnForNodes( e1, e2, 1, 1) ) snapped = true;
                  if( findConnForNodes( e1, e2, 1, 2) ) snapped = true;
                  if( findConnForNodes( e1, e2, 2, 1) ) snapped = true;
                  if( findConnForNodes( e1, e2, 2, 2) ) snapped = true;
                  if( findConnForNodes( e1, e2, 3, 1) ) snapped = true;
                  if( findConnForNodes( e1, e2, 3, 2) ) snapped = true;
              }
              else if( g2 instanceof ThreeNodeElemGeom){
                  if( findConnForNodes( e1, e2, 1, 1) ) snapped = true;
                  if( findConnForNodes( e1, e2, 1, 2) ) snapped = true;
                  if( findConnForNodes( e1, e2, 1, 3) ) snapped = true;
                  if( findConnForNodes( e1, e2, 2, 1) ) snapped = true;
                  if( findConnForNodes( e1, e2, 2, 2) ) snapped = true;
                  if( findConnForNodes( e1, e2, 2, 3) ) snapped = true;
                  if( findConnForNodes( e1, e2, 3, 1) ) snapped = true;
                  if( findConnForNodes( e1, e2, 3, 2) ) snapped = true;
                  if( findConnForNodes( e1, e2, 3, 3) ) snapped = true;
              }
              else{
                  System.out.println("Error @ Sys.findConnForTwoElem() -4");
              }
          }
          else{
              System.out.println("Error @ Sys.findConnForTwoElem() -5");
          }

          return snapped;
  }

  private static boolean findConnForNodes(AbsElem e1, AbsElem e2, int i, int j){
      int x,y, a,b;
      AbsElemGeom g1=e1.getElemGeomObj();
      AbsElemGeom g2=e2.getElemGeomObj();

      if(   ( i== 3 && !(g1 instanceof ThreeNodeElemGeom) ) ||
            ( j== 3 && !(g2 instanceof ThreeNodeElemGeom) ) ){
          System.out.println("Error occured @ Sys.findConnForNodes()");
          return false;
      }

      if      (i==1) { x= g1.getX1Stored();  y= g1.getY1Stored();}
      else if (i==2) { x= g1.getX2Stored();  y= g1.getY2Stored(); }
      else if (i==3) { x= ((ThreeNodeElemGeom)g1).getX3Stored();
                       y= ((ThreeNodeElemGeom)g1).getY3Stored();
                       //System.out.println("Here - i");
                       }
      else{// error
          System.out.println("Error Ocuured @ findConnForNodes()");
          return false;
      }

      if      (j==1) { a= g2.getX1Stored();  b= g2.getY1Stored();}
      else if (j==2) { a= g2.getX2Stored();  b= g2.getY2Stored(); }
      else if (j==3) { a= ((ThreeNodeElemGeom)g2).getX3Stored();
                       b= ((ThreeNodeElemGeom)g2).getY3Stored();
                       //System.out.println("Here - j");
                       }
      else{// error
          System.out.println("Error Ocuured @ findConnForNodes()");
          return false;
      }

      /*String s=g1.getName();
      if( (i== 1 && j==3) && (s.compareToIgnoreCase("Recloser  1673")==0) ){
            System.out.println("i="+i+" ; j="+j);
            System.out.println("i: ("+g1.x1Stored+","+g1.y1Stored+"),  "+
                                  "("+g1.x2Stored+","+g1.y2Stored+")");
            System.out.println("j: ("+g2.x1Stored+","+g2.y1Stored+"),  "+
                                  "("+g2.x2Stored+","+g2.y2Stored+"),  "+
                                  "("+((ThreeNodeElemGeom)g2).x3Stored+","+((ThreeNodeElemGeom)g2).y3Stored+")");
      }*/

      if( x == a && y == b){
          insertIntoConnList(e1, e2, i, j);
          return true;
      }
      else return false;
  }

  private static boolean findConnForBusses( Bus b1, Bus b2){
      BusGeom g1=(BusGeom)b1.getElemGeomObj();
      BusGeom g2=(BusGeom)b2.getElemGeomObj();
      //if( g1.isLineConnected( g2.x1, g2.y1,  g2.x2, g2.y2 ) == true){
      if( g1.isConnectedWithBus(g2, 0) == true){ //snap_size =0
            insertIntoConnList(b1, b2, 1, 1);
            return true;
      }
      else  return false;
  }

  private static boolean findConnForBusAndNode( Bus b, AbsElem e, int i){
      BusGeom gBus = (BusGeom) b.getElemGeomObj();
      AbsElemGeom gElem = e.getElemGeomObj();
      int x,y;
      if( i==1 ) {    x = gElem.x1Stored;  y = gElem.y1Stored;    }
      else if( i==2){ x = gElem.x2Stored;  y = gElem.y2Stored;    }
      else if( i==3){
            if( gElem instanceof ThreeNodeElemGeom ){
                x = ((ThreeNodeElemGeom)gElem).x3Stored;
                y = ((ThreeNodeElemGeom)gElem).y3Stored;
            }
            else{
                System.out.println("Error occured @ Sys.snapBusAndElem()-1");
                return false;
            }
      }
      else {//error
          System.out.println("Error occured @ Sys.snapBusAndElem()-2");
          return false;
      }

      if( gBus.coverPointBasedonStored(x,y)==true){
          insertIntoConnList( b, e, 1, i);
          return true;
      }
      else return false;
  }


  /*===========================================================
  1.buildSubTree( AbsElem)
  2.buildSubTree( Bus )
  They both are used by buildSysTopo() to build the radial tree
  ===========================================================*/
  private static int buildSubTree( AbsElem e){
        AbsElemGeom g = e.getElemGeomObj();
        Vector v;
        AbsElem e2;
        AbsElemGeom g2;
        //CktLaw ckt, ckt2;
        int res;

        //next line is a preventative, redundent code
        if( e instanceof Bus) { return buildSubTree( (Bus) e ); }

        if( e instanceof Switch3){
            if( ((ThreeNodeElemGeom)g).getIsSwitched() ==false ){// 1<==>3; 2 open
                if( g.sourceNode == 1 ) v=((ThreeNodeElemGeom)g).conn3;
                else if( g.sourceNode ==3) v =g.conn1;
                else return 1;  //this should not be reached
            }
            else{  // 2<==>3 ;  1 open
                if( g.sourceNode == 2 ) v=((ThreeNodeElemGeom)g).conn3;
                else if( g.sourceNode ==3) v =g.conn2;
                else return 1;  //this should not be reached
            }
        }
        else{
            if( g.sourceNode == 1) v= g.conn2;
            else v=g.conn1;
        }


        boolean hasBus = false;
        if( (v.size()>0 )  && v.elementAt(0) instanceof Bus ) hasBus = true;

        //e is a non-bus element
        for(int i=0;i<v.size();i++){
            e2=(AbsElem) v.elementAt(i);
            g2=e2.getElemGeomObj();

            if( e2 instanceof Bus ) {
                g2.parent = e; g.childList.add(e2);
                res = buildSubTree( (Bus) e2 );  //do for Bus
                if( res >1 ) return res;
                else continue;
                //else break;
            }
            else if( hasBus == true ) break;  //skip for all non-Bus element
                                              //in the case that v has Bus connected

            //following code to the end of for loop : v has no Bus element

            if( e2 instanceof Source) {
                displayLoopMsg( e, e2);
                return 6; //loop found
            }

            //for open switch: no recursion for an open swt
            if( (g2 instanceof SwtGeom) && ((SwtGeom)g2).getIsOpen()==true ){
                SwtGeom gS = (SwtGeom)g2;
                if( gS.parent ==null) { gS.parent = e; g.childList.add(e2); continue;}
                else if( gS.parent2 == null ) { gS.parent2 =e; g.childList.add(e2); continue; }
                else {
                    displayLoopMsg( e, e2);
                    return 7;  //loop founded
                }
            }

            //for transfer switch
            if( e2 instanceof Switch3 ){
                ThreeNodeElemGeom gS3 = ((ThreeNodeElemGeom)g2);
                if( gS3.getIsSwitched() == false ){//unswitched: 1<==>3; 2 open
                    if( gS3.conn1.contains( e) ) {
                        //gS3.parent2 = null;
                        if( gS3.parent !=null) {
                            displayLoopMsg( e, e2);
                            return 9;
                        } //loop existed
                        gS3.parent = e;
                        g.childList.add( e2 );
                        gS3.sourceNode = 1;
                        res = buildSubTree( e2 );
                        if( res >1) return res;
                    }
                    else if( gS3.conn2.contains(e)){
                        //if( gS3.parent2 != null ){
                        //    JOptionPane.showMessageDialog(mainFrame,
                        //       "Loop: Child="+Sys.elemList.indexOf(e)+"; Parent="+Sys.elemList.indexOf(e2) );
                        //    return 10;
                        //} //loop
                        gS3.parent2 = e; //due to the weird protocal of topo. in Engines
                        //g.childList.add( e2 );
                        continue;  // no recursion since this is an open point
                    }
                    else if( gS3.conn3.contains(e)){
                        if( gS3.parent !=null) {
                            displayLoopMsg( e, e2);
                            return 11;
                        } //loop existed
                        gS3.parent = e;
                        g.childList.add( e2 );
                        gS3.sourceNode=3;
                        res = buildSubTree( e2 );
                        if( res >1) return res;
                    }
                    else{
                        System.out.println("Error @ Sys.buildTree(AbsElem)");
                        return 100;
                    }
                }//end of unswitched Transfer_Switch
                else{ // is switched: 2<==>3 ; 1 open
                    if( gS3.conn1.contains( e) ) {
                        //if( gS3.parent !=null) {
                        //    JOptionPane.showMessageDialog(mainFrame,"Loop: Child="
                        //        +Sys.elemList.indexOf(e)+"; Parent="+Sys.elemList.indexOf(e2) );
                        //    return 12;
                        //} //loop existed
                        gS3.parent = e; //due to the weird protocal of topo. in Engines
                        //g.childList.add( e2 );
                        continue;  // no recursion since this is an open point
                    }
                    else if( gS3.conn2.contains(e)){
                        if( gS3.parent2 != null ){
                            displayLoopMsg( e, e2);
                            return 13;
                        } //loop
                        gS3.parent2 = e;
                        g.childList.add( e2 );
                        gS3.sourceNode=2;
                        res = buildSubTree( e2 );
                        if( res >1) return res;
                    }
                    else if( gS3.conn3.contains(e)){
                        if( gS3.parent !=null) {
                            displayLoopMsg( e, e2);
                            return 14;
                        } //loop existed
                        gS3.parent = e;
                        g.childList.add( e2 );
                        gS3.sourceNode=3;
                        res = buildSubTree( e2 );
                        if( res >1) return res;
                    }
                    else{
                        System.out.println("Error @ Sys.buildTree(AbsElem)");
                        return 100;
                    }
                }//end of if switched
                continue;
            }//end of if Transfer_Switch

            //for close switch or any other elements (Closed switches perfom the
            //same as other elements in sense of building a tree
            if( g2.parent != null) {
                displayLoopMsg( e, e2);
                return 8;
            } // loop founded
            else { g2.parent = e; g.childList.add(e2); }

            if( g2.conn1.contains( e ) ){ g2.sourceNode = 1;}
            else if( g2.conn2.contains(e) ){ g2.sourceNode =2;}
            else { return 2;} //error occured

            res = buildSubTree( e2);  //do recursion for closed swt & other elems
            if( res > 1){ return res;}
        }//end of for
        return 1;
  }//end of buildSubTree( AbsElem e)

  private static int buildSubTree(Bus e){
        AbsElemGeom g = e.getElemGeomObj();
        Vector v;
        AbsElem e2;
        AbsElemGeom g2;
        //CktLaw ckt, ckt2;
        int res;

        //Bus.geom.conn1 contains all connected non-Bus elements
        for( int j=0;j<g.conn1.size();j++){
            e2 = (AbsElem) g.conn1.elementAt(j);
            g2= e2.getElemGeomObj();

            if( e2 instanceof Bus ){  //when 2 or more bus are connected, the relation
                                      //between any two bus must be child-parent;
                                      //(or siblings)
                if(e2 == g.parent ) continue;  //parent-child

                if( g.parent.getElemGeomObj().conn1.contains(e) &&
                    g.parent.getElemGeomObj().conn1.contains(e2) ) continue;  //sibling
                if( g.parent.getElemGeomObj().conn2.contains(e) &&
                    g.parent.getElemGeomObj().conn2.contains(e2) ) continue;  //sibling

                //else
                {
                    g2.parent = e;
                    g.childList.add( e2 );
                    res = buildSubTree( (Bus)e2);  //do recursion for closed swt & other elems
                    if( res > 1){ return res;}
                }
            }
            else{//for non-Bus element
                //if e2 is the parent of e, skip it;
                //if e2.parent is a Bus && (e2.parent is sibling of e), skip e2
                if( e2 == g.parent ) { continue; }
                //else if( (g2.parent instanceof Bus) &&
                //          g2.parent.getElemGeomObj().conn1.contains(e2) )  continue;//connected bus
                else if( g.parent instanceof Bus &&
                         g.parent.getElemGeomObj().conn1.contains(e2) ){ continue;}

                else{
                    if( e2 instanceof Source ){
                        displayLoopMsg( e, e2);
                        return 3;
                    }//loop found

                    //for open switch: no recursion for an open swt
                    if( (g2 instanceof SwtGeom) && ((SwtGeom)g2).getIsOpen()==true ){
                        SwtGeom gS = (SwtGeom)g2;
                        if( gS.parent ==null) { gS.parent = e; g.childList.add(e2); continue;}
                        else if( gS.parent2 == null ) { gS.parent2 =e; g.childList.add(e2); continue; }
                        else {
                            displayLoopMsg( e, e2);
                            return 4;  //loop founded
                        }
                    }

                    //for close switch or any other elements (Closed switches perfom the
                    //same as other elements in sense of building a tree
                    if( g2.parent != null) {
                        displayLoopMsg( e, e2);
                        return 5;
                    } // loop founded
                    else { g2.parent = e; g.childList.add(e2); }

                    if( g2.conn1.contains( e ) ){ g2.sourceNode = 1;}
                    else if( g2.conn2.contains(e) ){ g2.sourceNode =2;}
                    else { return 2;} //error occured

                    res = buildSubTree( e2);  //do recursion for closed swt & other elems
                    if( res > 1){ return res;}
                }
            }
        }//end of for
        return 1;
  }//end of buildTree(Bus e)

  private static void displayLoopMsg( AbsElem childElem, AbsElem parentElem){
      String msg = "Loop: Child="+ ( Sys.elemList.indexOf(childElem)+1 );
      msg += "; Parent="+ ( Sys.elemList.indexOf(parentElem)+1 );
      JOptionPane.showMessageDialog(mainFrame, msg);
  }

  /*===================================================
  To add e1 into e2.geom.connX where X=j
  To add e2 into e1.geom.connY where Y=i
  Precondition: e1 and e2 are connected
  ===================================================*/
  private static void insertIntoConnList( AbsElem e1, AbsElem e2, int i, int j){
      AbsElemGeom g1 = e1.getElemGeomObj();
      AbsElemGeom g2 = e2.getElemGeomObj();
      Vector v1, v2;
      if(   ( i== 3 && !(g1 instanceof ThreeNodeElemGeom) ) ||
            ( j== 3 && !(g2 instanceof ThreeNodeElemGeom) ) ){
          System.out.println("Error occured @ Sys.insertIntoConnList()");
          return;
      }

      if( i==1) v1= g1.conn1;
      else if(i==2) v1 = g1.conn2;
      else if(i==3) v1=((ThreeNodeElemGeom)g1).conn3;
      else {
          System.out.println("Error occured @ Sys.insertIntoConnList()");
          return;
      }

      if( j==1) v2= g2.conn1;
      else if(j==2) v2 = g2.conn2;
      else if( j==3) v2=((ThreeNodeElemGeom)g2).conn3;
      else {
          System.out.println("Error occured @ Sys.insertIntoConnList()");
          return;
      }

      if( e1 instanceof Bus )   v2.add(0, e1);
      else v2.add(e1);

      //not used right now, becoz e2 should not be a Bus element
      if( e2 instanceof Bus )   v1.add(0, e2);
      else v1.add(e2);
  }

  public static void divideDataInPerUnitForVector( Vector v, double k) {
      AbsElem elem;
      int sz = v.size();
      for(int i=0; i<sz; i++){
          elem =(AbsElem) v.elementAt(i);
          elem.divideDataInPerUnit( k );
      }
  }//end of divideDataInPerUnitForVector( Vector v, double k);

  public static void divideDataInPerUnitForAllElem( double k) {
      divideDataInPerUnitForVector( Sys.elemList, k );
  }

  public static void multiplyLengthForVector( Vector v, double k) {
      AbsElem elem;
      int sz = v.size();
      for(int i=0; i<sz; i++){
          elem =(AbsElem) v.elementAt(i);
          if( elem instanceof Line &&
              elem.getAutoFlag(AbsElem.DATA_LENGTH) ) {
                  elem.multiplyData( AbsElem.DATA_LENGTH, k );
          }
      }
  }//end of multiplyLengthForVector( Vector v, double k);

  public static void multiplyLengthForAllElem( double k) {
      multiplyLengthForVector( Sys.elemList, k );
  }

  public static double findRatioBtwnUnits(int currUnit, int prevUnit){
      double k1=1.0, k2=1.0;
      switch(currUnit){
          case 0: k1=1.0;     break;
          case 1: k1=1.6093;  break;
          case 2: k1=5.280;   break;
          case 3: k1=5280.0;  break;
      }

      switch(prevUnit){
          case 0: k2=1.0;     break;
          case 1: k2=1.6093;  break;
          case 2: k2=5.280;   break;
          case 3: k2=5280.0;  break;
      }
      return k1/k2;
  }

  public static void computeGroupInfo(){
      AbsElem elem, e2;
      AbsElemGeom geom, g2;
      int sz = Sys.elemList.size();
      for(int i=0; i<sz; i++){
          elem = (AbsElem) elemList.elementAt(i);
          elem.getElemGeomObj().group = -1;
      }
      for(int i=0;i<sz;i++){
          elem = (AbsElem) elemList.elementAt(i);
          if( !(elem instanceof Group)) continue; //not Group
          geom = elem.getElemGeomObj();
          for(int k=0;k<sz;k++){
              e2 = (AbsElem) elemList.elementAt(k);
              g2 = e2.getElemGeomObj();
              if( geom.frame.contains(g2.x1, g2.y1) && geom.frame.contains(g2.x2,g2.y2)){
                  ((Group)elem).containedElemList.add( e2 );
                  g2.group = i;
              }
          }
        }//end of for
  }

    /**
     * The <code>Histogram</code> class performs a histogram over all or a
     * subset of all elements.
     */
    public static class Histogram {
        // input data
        private double dZone = Zone.ENTIRE_SYSTEM;
        private int iType    = SysConst.TYPE_ALL;
        private boolean bFieldIsResult = true;
        private int     iField         = -1;
        private boolean bWeightIsResult = false;
        private int     iWeight         = -1;
        private double dStart = 0.0;
        private double dEnd   = 0.0;
        // result data
        private double[] adBin   = null;
        private double dBelowBin = 0;
        private double dAboveBin = 0;
        private int iCount       = 0;
        private double dTotalBin = 0.0;

        public Histogram() {}

        /**
         * Sets the Zone which should be included in the histogram.<p>
         * Default value: <code>Zone.ENTIRE_SYSTEM</code>
         */
        public final void setZone(double zone) { dZone = zone; }
        public final double getZone() { return dZone; }

        /**
         * Sets the type of element that should be included in the histogram.<p>
         * Default value: <code>SysConst.TYPE_ALL</code>
         */
        public final void setType(int type) { iType = type; }
        public final int getType() { return iType; }

        /**
         * Sets the data or result field that should be considered.<p>
         * Must be set!
         */
        public final void setField(int iIndex, boolean isResult) {
            bFieldIsResult = isResult;
            iField = iIndex;
        }
        public final int      getField() { return iField; }
        public final boolean  getFieldIsResult() { return bFieldIsResult; }

        /**
         * Sets the weight field with which every element is weighted.<p>
         * Default value: null, which means a weight of 1.0
         */
        public final void setWeight(int  iIndex,  boolean isResult) {
            bWeightIsResult = isResult;
            iWeight = iIndex;
        }
        public final int      getWeight() { return iWeight; }
        public final boolean  getWeightIsResult() { return bWeightIsResult; }

        /**
         * Sets the start value for the histogram.
         * @see findStartEnd()
         */
        public final void setStart(double start) { dStart = start; }
        public final double getStart() { return dStart; }

        /**
         * Sets the end value for the histogram.
         * @see findStartEnd()
         */
        public final void setEnd(double end) { dEnd = end; }
        public final double getEnd() { return dEnd; }

        /**
         * Sets the number of bins for the histogram.<p>
         * Must be set!
         */
        public final void setBinCount(int count) { adBin = new double[count]; }
        public final int getBinCount() { return adBin.length; }

        /**
         * Returns the size of one bin.
         */
        public final double getBinSize() { return (dEnd-dStart)/adBin.length; }
        /**
         * Returns the lower limit of the bin with the given index.
         */
        public final double getLowerBinLimit(int index) {
            return dStart + index * getBinSize();
        }
        /**
         * Returns the upper limit of the bin with the given index.
         */
        public final double getUpperBinLimit(int index) {
            return dStart + (index + 1) * getBinSize();
        }

        /**
         * Returns the bin content of the bin with the given index.<p>
         * Only valid after the histogram is built.
         */
        public final double getBin(int index) { return adBin[index]; }
        /**
         * Returns the bin content of the pseudo bin below the start value.<p>
         * Only valid after the histogram is built.
         */
        public final double getBelowBin() { return dBelowBin; }
        /**
         * Returns the bin content of the pseudo bin above the end value.<p>
         * Only valid after the histogram is built.
         */
        public final double getAboveBin() { return dAboveBin; }

        /**
         * Returns the total number of all elements used for the histogram.<p>
         * Only valid after <code>findStartEnd()</code> is called or the
         * histogram is built.
         */
        public final int getCount() { return iCount; }
        /**
         * Returns the total content of all bins.<p>
         * Only valid after <code>findStartEnd()</code> is called or the
         * histogram is built.
         */
        public final double getTotalBin() { return dTotalBin; }

        /**
         * Determines the start and end value so that all elements are inside
         * these limits.
         * @see setStart
         * @see setEnd
         */
        public void findStartEnd() {
            dStart = dEnd = 0.0;
            iCount = 0;
            for (int i = 0; i < Sys.elemList.size(); i++) {
                AbsElem elem = (AbsElem)Sys.elemList.elementAt(i);
                if (Zone.getZoneFits(dZone, elem) &&
                    (iType == SysConst.TYPE_ALL || iType == elem.getType())) {
                    double dFieldValue = getFieldValue(elem);
                    if (iCount == 0) {
                        dStart = dEnd = dFieldValue;
                    } else {
                        if (dFieldValue < dStart)
                            dStart = dFieldValue;
                        else if (dFieldValue > dEnd)
                            dEnd = dFieldValue;
                    }
                    iCount++;
                }
            }
        }

        /**
         * Returns the mean of all weighted elements.
         */
        public double getMean() {
            double dTotalProduct = 0.0;
            dTotalBin = 0.0;
            for (int i = 0; i < Sys.elemList.size(); i++) {
                AbsElem elem = (AbsElem)Sys.elemList.elementAt(i);
                if (Zone.getZoneFits(dZone, elem) &&
                    (iType == SysConst.TYPE_ALL || iType == elem.getType())) {
                    double dWeightValue = getWeightValue(elem);
                    dTotalProduct += dWeightValue * getFieldValue(elem);
                    dTotalBin     += dWeightValue;
                }
            }
            return (dTotalBin == 0.0 ? 0.0 : dTotalProduct / dTotalBin);
        }

        /**
         * Builds the histogram according to the given parameters.
         */
        public void buildHistogram() {
            dBelowBin = 0.0;
            dAboveBin = 0.0;
            iCount    = 0;
            dTotalBin = 0.0;

            if (adBin == null)
                return;

            // use precalculated multiplier for performance reasons
            double dMultiplier = 0.0;
            if (dEnd != dStart)
                dMultiplier = adBin.length / (dEnd - dStart);
            for (int i = 0; i < Sys.elemList.size(); i++) {
                AbsElem elem = (AbsElem)Sys.elemList.elementAt(i);
                if (Zone.getZoneFits(dZone, elem) &&
                    (iType == SysConst.TYPE_ALL || iType == elem.getType())) {
                    double dFieldValue = getFieldValue(elem);
                    double dWeight = getWeightValue(elem);
                    if (dFieldValue < dStart)
                        dBelowBin += dWeight;
                    else if (dFieldValue >= dEnd)
                        dAboveBin += dWeight;
                    else {
                        int iBin = (int)((dFieldValue - dStart) * dMultiplier);
                        adBin[iBin] += dWeight;
                    }
                    iCount++;
                    dTotalBin += dWeight;
                }
            }
        }

        private final double getFieldValue(AbsElem elem) {
            if (iField < 0)
                return 0.0;
            else if (bFieldIsResult)
                return elem.getResult(iField);
            else
                return elem.getData(iField);
        }

        private final double getWeightValue(AbsElem elem) {
            if (iWeight < 0)
                // Default weight is 1.0 !
                return 1.0;
            else if (bWeightIsResult)
                return elem.getResult(iWeight);
            else
                return elem.getData(iWeight);
        }
    }
}//end of class Sys

